package src.event;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;

/**
 * @author Brent
 *
 * <b>Overview</b>
 * The EventHandlers are the class of objects that take a custom event type and deliver it to all of its registered EventListenrs.  By extending java.Thread, it gains the ability to distribute these messages asynchronously or immediately, depending on the method used to fire the event.
 *
 * <b>Responsibilities</b>
 * This class is responsible for Taking Events of a particular type and distributing them to all of the registered EventListeners that correspond to that type of Event.  It has the ability to do this both synchronously or asynchronously.
 *
 * <b>Collaborators</b>
 * EventListener&lt;E extends Event&gt; - Client: relies on EventListeners to react to events once they are fired
 *
 * <b>Implementor</b> - Brent
 * <b>Tester</b> - Tim
 *
 * @param <E>
 */
public class EventHandler<E extends Event> extends Thread {
	private ArrayList<EventListener<E>> listeners;
	private Queue<E> eventQueue;
	private boolean eventsPending;
	private boolean handlingEvents;

	/**
	 * Constructs a new EventHandler
	 */
	public EventHandler()
	{
		listeners = new ArrayList<EventListener<E>>();
		eventQueue = new LinkedList<E>();
		handlingEvents = true;

		this.start();
	}

	/**
	 * Registers a new Listener to recieve event notifications
	 *
	 * @param el - the EventListener to be registered
	 */
	public void regesterListener(EventListener<E> el)
	{
		listeners.add(el);
	}

	/**
	 * Asynchronolusly fires an event
	 *
	 * @param e - the Event to be fired
	 */
	public void fireEvent(E e)
	{
		eventQueue.offer(e);
		eventsPending = true;
	}

	/**
	 * Synchronolusly fires an event
	 * @param e - the Event to be fired
	 * @return true if and only if ALL listeners handle the event sucessfully
	 */
	public boolean fireEventNow(E e)
	{
		return dispatchEvent(e);
	}

	/* (non-Javadoc)
	 * @see java.lang.Thread#run()
	 */
	public void run()
	{
		while(handlingEvents)
		{
			if(eventsPending) dispatchEvent(eventQueue.poll());
		}
	}

	/**
	 * terminates the event handling thread
	 */
	public void terminate()
	{
		handlingEvents = false;
	}

	/**
	 * dispatches an event
	 *
	 * @param e - the event to be dispatched
	 * @return true if and only if ALL listeners handle the event sucessfully
	 */
	private boolean dispatchEvent(E e)
	{
		boolean handled = true;
		for(EventListener<E> el : listeners)
		{
			if(!el.eventCallback(e)) handled = false;
		}

		if (eventQueue.peek() == null) eventsPending = false;

		return handled;
	}

}
